#include "Expectimax.h"
#include <exception>
#include <cfloat>
#include "CommonApps.h"
//C'tor
//Expectimax::Expectimax(SenetHeuristics& heurisitic): m_heuristic(heurisitic){}

void Expectimax::Init(const SenetPlayerExt* owner , int gameStickNum)
{
	m_gameStickNum = gameStickNum;
	m_gameTwoThirdStickNum = ((2*m_gameStickNum) / 3);
	m_probabilities.init(gameStickNum);
	m_heuristic.init(owner , m_probabilities); 
	
}

void Expectimax::InitHeuristicParams(const float* heuristicParams)
 {
	 HeuristicParams params;
	 float copiedParams[params.numSubHeuristics][params.numPlayers];
	 //copy heuristics params
	 for(int i=0 ; i< params.numPlayers; ++i)
	 {
		 for(int j = 0; j< params.numSubHeuristics; ++j)
		 copiedParams[j][i] = heuristicParams[j];
	 }
	 params.setCoefficients(copiedParams);
//	 m_heuristic.setHeuristicParams(params);
 }

// Assignment operator
Expectimax& Expectimax::operator =(const Expectimax& e)
{
	m_heuristic = e.m_heuristic;
	m_gameStickNum = e.m_gameStickNum;
	return *this;
}


// Calculate minimax 
void Expectimax::minimax(Score& res , SenetBoardDraft& board, int depth, const GameTimer& timer,  bool checkTime , 
					Definitions::PlayerColor currentPlayer, int stickNum , NodeType nodeType) throw (Exception)
{
	if(checkTime && timer.isMoveTimePassed(GameTimer::NO_OVERHEAD))
	{
		throw Exception::TIME_OUT;
	}
	if(board.isTerminal() || depth == 0)
	{
		res.pieceToMove = -1;
		res.score = m_heuristic.score(board);
		return;
	}
	
	vector<int> moves;
	board.generateMoves(moves,currentPlayer,stickNum);
	int movesSize = moves.size();
	if(movesSize == 0)
	{
		//TODO: consider to continue the search to collect data for next runs
		res.pieceToMove = -1;
		res.score = m_heuristic.score(board);
		return;
	}
	Definitions::PlayerColor nextPlayer;
	NodeType nextNodeType;
	if(stickNum == m_gameTwoThirdStickNum || stickNum == 1 || stickNum == m_gameStickNum)
	{
		//next turn I play again
		nextPlayer = currentPlayer;
		nextNodeType = nodeType;
	}
	else
	{
		//next turn my opponent plays
		nextPlayer = Definitions::getOppositePlayer(currentPlayer);
		nextNodeType = CommonApps::getOppositeNodeType(nodeType);
	}
	//there are moves to check
	int startLoc;
	int endLoc;
	float best = 0;
	int pieceToMove = -1;
	if(nodeType == MAX)
	{
		best = -FLT_MAX;

		for(int i = 0 ; i < movesSize ; ++i)
		{
			startLoc = moves[i];
			endLoc = startLoc + stickNum;
			
			//do move
			vector<SenetBoardDraftMove> movesPerformed;
			board.doMove(movesPerformed , startLoc , endLoc , currentPlayer);
			
			//score move
			Score val = chanceSearch(board , depth-1 , timer, checkTime , nextPlayer,
									-1 , CHANCE, nextNodeType);
			//undo move
			CommonApps::undoMoves(movesPerformed, board , currentPlayer);

 			if(val.score > best)
			{
				best = val.score;
				pieceToMove = startLoc;
			}
		}
	}
	else if(nodeType == MIN)
	{
		best = FLT_MAX;
		for(int i = 0; i < movesSize ; ++i)
		{
			startLoc = moves[i];
			endLoc = startLoc + stickNum;
			
			//do move
			vector<SenetBoardDraftMove> movesPerformed;
			board.doMove(movesPerformed , startLoc , endLoc , currentPlayer);
			
			//score move
			Score val = chanceSearch(board, depth-1 , timer , checkTime , nextPlayer ,
									-1 , CHANCE , nextNodeType);
			//undo moves
			CommonApps::undoMoves(movesPerformed, board , currentPlayer);

			if(val.score < best)
			{
				best = val.score;
				pieceToMove = startLoc;
			}
		}
	}
	
	res.score = best;
	res.pieceToMove = pieceToMove;
}


//calculate value at chance nodes
void Expectimax::expectimax(Score& res, SenetBoardDraft &board, int depth, const GameTimer& timer, bool checkTime , 
						Definitions::PlayerColor nextPlayer, NodeType nextTurn) throw (Exception)
{
	if(checkTime && timer.isMoveTimePassed(GameTimer::NO_OVERHEAD))
	{
		throw Exception::TIME_OUT;
	}
	if(board.isTerminal() || depth == 0)
	{
		res.pieceToMove = -1;
		res.score = m_heuristic.score(board);
	}
	else
	{
		//more resources to use - continue search
		float sum = 0;
		for(int i = 0; i <= m_gameStickNum; ++i)
		{
			Score val = chanceSearch(board, depth-1  , timer , checkTime , nextPlayer , i , nextTurn , DONT_CARE);
			sum += m_probabilities.getProbability(i) * val.score;		
		}
		res.score = sum;
		res.pieceToMove = -1; 
	}	
}


//driver function
Score Expectimax::chanceSearch(SenetBoardDraft& board, int depth, const GameTimer& timer, bool checkTime ,
						Definitions::PlayerColor currentPlayer, int stickNum ,  NodeType nodeType , NodeType nextTurn) throw(Exception)
{	
	Score res;
	if(nodeType == CHANCE)
	{
		expectimax(res , board, depth, timer, checkTime, currentPlayer, nextTurn);
	}
	else
	{
		minimax(res , board, depth, timer, checkTime, currentPlayer, stickNum , nodeType);
	}
	return res;
	
}


//main function - init turn parameters and calculation
SenetMove Expectimax::scoreExpectimax(SenetBoardDraft& board, int depth, const GameTimer& timer , 
									  bool checkTime , Definitions::PlayerColor myColor, int stickNum) throw(Exception)
{	
	if(stickNum == 0)
	{
		//TODO: see that star1 and improvements do the same for empty move
		SenetMove resMove(-1);	
		return resMove;
	}
	Score res =  chanceSearch(board, depth, timer, checkTime , myColor, stickNum, MAX , DONT_CARE);

	m_FinalBoardScore = res.score;

	SenetMove resMove(res.pieceToMove);	
	return resMove;

}
